require 'uri'
require 'net/http'
module ESI
  module Tag

    #
    #
    #  ir = IncludeRequest.new( {'header1'=>'value1'} )
    #
    #  ir.request( '/fragment' ) do|status,response|
    #     if status 
    #         response.read_body do|str|
    #         end
    #     else
    #         # error case
    #     end
    #  end
    #
    class IncludeRequest
      class Error
        attr_reader :message, :response
        def initialize(msg,response)
          @message = msg
          @response = response
        end
      end
      attr_reader :exception, :overflow_index # TODO

      def initialize(forward_headers)
        @headers = forward_headers
      end

      def request(uri, timeout = 1, alt_failover=nil, follow_limit=3)
        uri = URI.parse(uri) if uri.is_a?(String)
        http = Net::HTTP.new(uri.host, uri.port)
        http.read_timeout = timeout
        http.get2( uri.to_s, @headers ) do|response|
          case response
          when Net::HTTPSuccess
            yield true, response
          when Net::HTTPRedirection
            request(URI.parse(response['location']), timeout, alt_failover, follow_limit - 1) do|s,r|
              yield s, r
            end
          else
            if alt_failover
              request(alt_failover, timeout, nil, follow_limit) do|s,r|
                yield s, r
              end
            else
              yield false, Error.new("Failed to request fragment: #{uri.scheme}://#{uri.host}:#{uri.port}#{uri.path}", response)
            end
          end
        end
      rescue Timeout::Error => e
        yield false, Error.new("Failed to request fragment: #{uri.scheme}://#{uri.host}:#{uri.port}#{uri.path}, timeout error: #{e.message}", nil)
      end

    end

    class Include < Base
      def close( output, options = {} )
        super(output)
        raise_on_error = options[:raise] || false
        ir = IncludeRequest.new(@headers)

        src = @router.url_for(prepare_url_vars(@attributes["src"]))
        alt = @attributes['alt']
        alt = @router.url_for(prepare_url_vars(alt)) if alt

        if @cache.cached?( src, @headers )
          parser = ESI::StreamParser.new(@router,@cache)
          parser.instance_variable_set(:@stream_index, 0 )
          
          cached = @cache.get(url,@fragment_params).body

          parser.parse_buffer( ESI::Parser.new(@cache,@router,@headers,@http_params),
                               cached, output, 1 )
          
        else

          ir.request(src, @attributes['timeout'], alt ) do|status,response|
            if status
              # to support multiple depths of esi tags
              # TODO: this is total hackage, need to figure out how to support max depth limits...
              #parser = ESI::StreamParser.new(@router,@cache)
              #parser.instance_variable_set(:@stream_index, 0 )
             # 
             # response_body = response.read_body
              # TODO: cache ttl
             # @cache.put(src, @headers, 600, response_body )

             # parser.parse_buffer( ESI::Parser.new(@cache,@router,@headers,@http_params),
             #                      response_body, output, 1 )

                # TODO: need to reprocess the esi:include response to process any more esi tags... or decide
                # if this is a feature worth supporting...
                # also... would be good if we didn't need to buffer the response here and could stream it into 
                # the cache
                cache_buffer = ""
                response.read_body do|s|
                  output << s
                end
                @cache.put(src, @headers, 600, cache_buffer )
   
            else
              # error/ check if the include has an onerror specifier
              return if @attributes['onerror'] == 'continue'
              # response is an IncludeRequest::Error
              raise response if raise_on_error
              # stop processing and return the error object
              return response
            end
          end
        end
      end
    end

  end
end
